/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: main.cpp,v 1.21 2009-05-13 07:18:20 amaula Exp $
 * 
 * \file
 * \brief Main program for MaCI Launcher
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#include "executor.hpp"
#include "directory.hpp"
#include "launcher.hpp"
#include "owndebug.h"
#include "ownutils.h"
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <assert.h>
#include <signal.h>
#include "Launcher_build.h"

//*****************************************************************************
enum EOperatingMode { KModeExecute, KModeBuild };
//*****************************************************************************
static const char launcher_version[] = "Launcher v0.6.2 (Build " BUILD_NUMBER_STR_Launcher ")";
//*****************************************************************************
//*****************************************************************************
static int verbose = 1;
static TStringArray argumentArray;
static CLauncher launcher;
static EOperatingMode operatingMode = KModeExecute;
static std::string iDefaultMachineHost = "";
static unsigned int iDefaultMachinePort = 0;
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

static void showUsage()
{
  fprintf(stderr,"\n%s\n\n", launcher_version);
  fprintf(stderr,"Usage:\tLauncher [OPTIONS]\n");
  fprintf(stderr,"  [-M machinefile]\t\tExecute machine at 'machinefile'\n");
  fprintf(stderr,"  [-B machinefile]\t\tBuild machine defined by 'machinefile'\n");

  fprintf(stderr,"  [-U host]\t\tDefault GIMnetAP host. (If given, overrides machineconfig)\n");
  fprintf(stderr,"  [-P port]\t\tDefault GIMnetAP port. (If given, overrides machineconfig)\n");
  
  fprintf(stderr,"  [-v]\t\t\t\tIncrease verbosity by 1\n");
  fprintf(stderr,"  [-q]\t\t\t\tQuiet mode\n");
  fprintf(stderr, "\n\n");
}
//*****************************************************************************

static void buildMachine(const std::string &aMachinePath)
{  
  // Build machine
  dPrint(ODSTATUS, "Building Machine: '%s'", aMachinePath.c_str());
  if (launcher.BuildMachine(aMachinePath) == false) {
    dPrint(ODINFO,"Machine Build Failed.");
    
  } else {    
    dPrint(ODSTATUS,"Machine succesfully built.");
  
  }
  
}
//*****************************************************************************

static void executeMachine(const std::string &aMachinePath)
{

  // Pass in default host & port if manually set on command line.
  if (iDefaultMachineHost.size()) 
    launcher.SetDefaultGIMnetAPHost(iDefaultMachineHost);

  if (iDefaultMachinePort) 
    launcher.SetDefaultGIMnetAPPort(iDefaultMachinePort);

  // Execute machine
  dPrint(1, "Executing Machine: '%s'", aMachinePath.c_str());
  if (launcher.ExecuteMachine(aMachinePath) == false) {
    dPrint(2,"Launcher failed.");
  
  } else {

    // Run output in loop, print in level 1, with timeout in 500ms
    do {
      launcher.ReadAndPrintMachineOutput(1, 500);
    } while(launcher.WaitMachine(false) == false);

    launcher.ReadAndPrintMachineOutput(1, 100);
    
    dPrint(1,"Machine has terminated.");
  }
}
//*****************************************************************************

static void sig_handler(int /*signumber*/, siginfo_t *info, void* /* context */)
{
  dPrint(1,"Received signal '%d' (%s)", info->si_signo, strsignal(info->si_signo));
  switch(info->si_signo) {
  case SIGINT:
  case SIGTERM:
  default:
    dPrint(1,"Terminating due to signal");
    launcher.TerminateMachine();
    int x;
    launcher.WaitMachine(x);
    dPrint(1,"Exit.");
    exit(1);
    break;
  }

  return;
}
//*****************************************************************************

int main(int argc, char *argv[])
{
  // Init Debug lib
  debugInit();
  debugSetGlobalDebugLvl(1);
  debugSetLogFilename("MaCI-Launcher.log");
  
  // Turn on signal-handling
  struct sigaction siga;
  sigemptyset(&siga.sa_mask);
  siga.sa_flags = SA_SIGINFO;
  siga.sa_sigaction = sig_handler;
  
  // Set signals we can handle
  sigaction(SIGINT, &siga, NULL);
  sigaction(SIGTERM, &siga, NULL);

  // Machine to use.
  std::string machine;

  // Parse parameters
  bool parse = true;
  while(parse) {
    int c;
    c = getopt(argc, argv, "hvqf:U:P:E:A:M:B:L:");
    switch(c) {
      //////////////////////////////////
      //// Application specific     ////
      //////////////////////////////////
    case 'U': {
      iDefaultMachineHost = optarg;
      dPrint(ODINFO,"Using '%s' as default GIMnetAP host for machine(s)",
             iDefaultMachineHost.c_str());
      break;
    }
    case 'P': {
      iDefaultMachinePort = atoi(optarg);
      dPrint(ODINFO,"Using '%d' as default GIMnetAP port for machine(s)",
             iDefaultMachinePort);
      break;
    }
    case 'M': {
      machine = optarg;
      operatingMode = KModeExecute;
      break;
    }

    case 'B': {
      machine = optarg;
      operatingMode = KModeBuild;
      break;
    }

      //////////////////////////////////
      //// GENERIC:                 ////
      //////////////////////////////////
    case 'v':
      // Increase only if not previously set to 0 (by 'quiet')
      if (verbose > 0)
	debugSetGlobalDebugLvl(++verbose);
      break;
      
    case 'q':
      verbose = 0;
      debugSetGlobalDebugLvl(0);
      break;
      
    case 'h':
    case '?':
      showUsage();
      exit(1);
      break;
      
    case -1:
      parse = false;
      break;
    }
  }



  if (machine.size()) {
    switch(operatingMode) {
    case KModeExecute:
      executeMachine(machine);
      break;
    case KModeBuild:
      buildMachine(machine);
      break;
    }
  } else {
    showUsage();
  }

  dPrint(1,"Exit.");
  debugDeinit();

  return 0;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
